#include "smart_ptr.h"
#include <memory>
#include <iostream>

void CSmartPtr::testAutoPtr()
{
#if _MSC_VER < 1916 // VS C++17
    std::auto_ptr<int> sp1(new int(8));
    std::auto_ptr<int> sp2;
    sp2.reset(new int(9));
#endif
}

void CSmartPtr::testAutoPtr2()
{
#if _MSC_VER < 1916
    std::auto_ptr<int> sp1(new int(8));
    std::auto_ptr<int> sp2(sp1);
    if (sp1.get() != NULL) {
        std::cout << "sp1 is not empty." << std::endl;
    } else {
        std::cout << "sp1 is empty." << std::endl;
    }

    if (sp2.get() != NULL) {
        std::cout << "sp2 is not empty." << std::endl;
    } else {
        std::cout << "sp2 is empty." << std::endl;
    }

    std::auto_ptr<int> sp3(new int(8));
    std::auto_ptr<int> sp4;
    sp4 = sp3;
    if (sp3.get() != NULL) {
        std::cout << "sp3 is not empty." << std::endl;
    } else {
        std::cout << "sp3 is empty." << std::endl;
    }

    if (sp4.get() != NULL) {
        std::cout << "sp4 is not empty." << std::endl;
    } else {
        std::cout << "sp4 is empty." << std::endl;
    }
#endif
}

std::unique_ptr<int> func(int val)
{
    std::unique_ptr<int> up(new int(val));
    return up;
}

void CSmartPtr::testUniquePtr3()
{
    std::unique_ptr<int> sp1(std::make_unique<int>(123));

    // error such code
    //std::unique_ptr<int> sp2(sp1);
    //std::unique_ptr<int> sp3;
    //sp3 = sp1;

    std::unique_ptr<int> sp2 = func(123);
    std::unique_ptr<int> sp3(std::move(sp2));
    std::unique_ptr<int> sp4;
    sp4 = std::move(sp3);

    if (sp1.get() != nullptr) {
        std::cout << "sp1 is not empty." << std::endl;
    } else {
        std::cout << "sp1 is empty." << std::endl;
    }

    if (sp2.get() != nullptr) {
        std::cout << "sp2 is not empty." << std::endl;
    } else {
        std::cout << "sp2 is empty." << std::endl;
    }

    if (sp3.get() != nullptr) {
        std::cout << "sp3 is not empty." << std::endl;
    } else {
        std::cout << "sp3 is empty." << std::endl;
    }

    if (sp4.get() != nullptr) {
        std::cout << "sp4 is not empty." << std::endl;
    } else {
        std::cout << "sp4 is empty." << std::endl;
    }
}

void CSmartPtr::testUniquePtr4()
{
    std::unique_ptr<int[]> sp1(new int[10]);

    std::unique_ptr<int[]> sp2;
    sp2.reset(new int[10]);

    std::unique_ptr<int[]> sp3(std::make_unique<int[]>(10));

    for (int i = 0; i < 10; ++i) {
        sp1[i] = i;
        sp2[i] = i;
        sp3[i] = i;
    }

    for (int i = 0; i < 10; ++i) {
        std::cout << sp1[i] << ", " << sp2[i] << ", " << sp3[i] << std::endl;
    }
}

class Socket
{
public:
    Socket() {}
    ~Socket() {}

public:
    void close() {}
};

void CSmartPtr::testUniquePtr5()
{
    auto deletor = [] (Socket * pSocket) {
        pSocket->close();
        delete pSocket;
    };

    std::unique_ptr<Socket, decltype(deletor)> sp1(new Socket, deletor);
}

class CSharedPtrA
{
public:
    CSharedPtrA()
    {
        std::cout << "CSharedPtrA construcor" << std::endl;
    }

    ~CSharedPtrA()
    {
        std::cout << "CSharedPtrA destructor" << std::endl;
    }

private:

};

void CSmartPtr::testSharedPtr6()
{
    std::shared_ptr<CSharedPtrA> sp1(new CSharedPtrA);
    std::cout << "use count: " << sp1.use_count() << std::endl;

    std::shared_ptr<CSharedPtrA> sp2(sp1);
    std::cout << "use count: " << sp1.use_count() << ", " << sp2.use_count() << std::endl;

    sp2.reset();
    std::cout << "use count: " << sp1.use_count() << ", " << sp2.use_count() << std::endl;

    {
        std::shared_ptr<CSharedPtrA> sp3 = sp1;
        std::cout << "use count: " << sp1.use_count() << ", " << sp2.use_count() << ", " << sp3.use_count() << std::endl;
    }
    std::cout << "use count: " << sp1.use_count() << ", " << sp2.use_count() << std::endl;
}

class CSharedPtrB : public std::enable_shared_from_this<CSharedPtrB>
{
public:
    CSharedPtrB()
    {
        std::cout << "CSharedPtrB constructor" << std::endl;
    }

    ~CSharedPtrB()
    {
        std::cout << "CSharedPtrB destructor" << std::endl;
    }

public:
    std::shared_ptr<CSharedPtrB> getSelf()
    {
        return shared_from_this();
    }
};

void CSmartPtr::testSharedPtr7()
{
    std::shared_ptr<CSharedPtrB> sp1(new CSharedPtrB);
    std::shared_ptr<CSharedPtrB> sp2 = sp1->getSelf();
    std::cout << "use count: " << sp1.use_count() << ", " << sp2.use_count() << std::endl;
}

class CSharedPtrC : public std::enable_shared_from_this<CSharedPtrC>
{
public:
    CSharedPtrC()
    {
        m_i = 9;
        std::cout << "CSharedPtrC constructor" << std::endl;
    }

    ~CSharedPtrC()
    {
        m_i = 0;
        std::cout << "CSharedPtrC destructor" << std::endl;
    }

public:
    void func()
    {
        m_self_ptr = shared_from_this();
    }

private:
    int m_i;
    std::shared_ptr<CSharedPtrC> m_self_ptr;
};

void CSmartPtr::testSharedPtr8()
{
    std::shared_ptr<CSharedPtrC> spa(new CSharedPtrC);
    spa->func();
}

void CSmartPtr::testWeakPtr8()
{
    std::shared_ptr<int> sp1(new int(123));
    std::cout << "use count: " << sp1.use_count() << std::endl;

    std::weak_ptr<int> sp2(sp1);
    std::cout << "use count: " << sp1.use_count() << ", " << sp2.use_count() << ", " << sp2.expired() << std::endl;

    std::weak_ptr<int> sp3 = sp1;
    std::cout << "use count: " << sp1.use_count() << ", " << sp2.use_count() << ", " << sp2.expired() << std::endl;

    std::weak_ptr<int> sp4 = sp2;
    std::cout << "use count: " << sp1.use_count() << ", " << sp2.use_count() << ", " << sp2.expired() << std::endl;
}

class CWeakPtrA
{
public:
    void doSomething() {}
};

void CSmartPtr::testWeakPtr9()
{
    std::shared_ptr<CWeakPtrA> sp1(new CWeakPtrA);
    std::weak_ptr<CWeakPtrA> sp2(sp1);

    if (!sp1) {
        ;
    } else {
        sp1->doSomething();
        (*sp1).doSomething();
    }

#if 0
    if (!sp2) {
        ;
    } else {
        sp2->doSomething();
        (*sp2).doSomething();
    }
#endif
}

void CSmartPtr::testSmartPtr10()
{
    std::shared_ptr<int> sp0;
    std::shared_ptr<std::string> sp1;
    sp1.reset(new std::string);
    std::unique_ptr<int> sp2;
    std::weak_ptr<int> sp3;

    std::cout << "sp0 size: " << sizeof(sp0) << std::endl;
    std::cout << "sp1 size: " << sizeof(sp1) << std::endl;
    std::cout << "sp2 size: " << sizeof(sp2) << std::endl;
    std::cout << "sp3 size: " << sizeof(sp3) << std::endl;
    std::cout << "std::string size: " << sizeof(std::string) << std::endl;
}

class CSmartPtrSubscriber
{
public:
    CSmartPtrSubscriber()
    {
        std::cout << "constructor" << std::endl;
    }
    ~CSmartPtrSubscriber()
    {
        std::cout << "destructor" << std::endl;
    }

public:
    void doSomething()
    {
        std::cout << "do something..." << std::endl;
    }
};

void CSmartPtr::testSmartPtr11()
{
    CSmartPtrSubscriber* pSubscriber = new CSmartPtrSubscriber;
    std::shared_ptr<CSmartPtrSubscriber> spSubscriber(pSubscriber);

#if 0
    delete pSubscriber;
#endif

    CSmartPtrSubscriber* ptr = spSubscriber.get();

    std::shared_ptr<CSmartPtrSubscriber> sp1(new CSmartPtrSubscriber);
    //std::shared_ptr<CSmartPtrSubscriber> sp2 = sp1;
    const auto& sp3 = sp1;
    sp1.reset();
    std::cout << "sp1 use count: " << sp1.use_count() << ", " << sp1.unique() << ", " << sp3.use_count() << std::endl;
    sp3->doSomething();
}